#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <string.h>
#include <fcntl.h>
#include <ctype.h>

#include <errno.h>

//测试首先，启动service.c

#define SERPORT		9003
#define SERADDR		"192.168.105.10"		// ifconfig看到的
#define BACKLOG		100

//tcp server端口有2个套接字，监听的和通信的

char recvbuf[100]={0};

int main(int argc, const char* argv[])
{
	// 第1步：先socket打开文件描述符
	int sockfd = -1, ret = -1, clifd = -1;
	socklen_t len = 0;
	/****
	 * 
	 * struct sockaddr_in  网络编程接口用来表示IP地址的标准结构体
	 */
	struct sockaddr_in seraddr = {0};
	
	
	char ipbuf[30] = {0};
  /****
   *  socket 函数用于打开一个网络连接，成功返回一个网络文件描述符，也是int类型， 以后操作网络连接都通过这个网络描述符   
   *    int socket(int domain, int type, int protocol);
   * 
   *    AF_INET             IPv4 Internet protocols          ip(7)
        AF_INET6            IPv6 Internet protocols          ipv6(7)
   * 
   * domain： 域， 哪类网络ipv4 ipv6...
   *    type : 类型 
   *     SOCK_STREAM ： 表示使用tcp协议，可靠的
   *     SOCK_DGRAM ：  udp的协议， 不可靠的     
   * protocol ： 协议， 0 默认协议  ，自动选择
   * sockfd = 3  默认 012 已经被使用了  
   */	
// IPV4(可以IPV6)  流式套接子(用于tcp)   0表示根据第2个参数推断tcp
	sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if (-1 == sockfd)
	{
		perror("socket");
		return -1;
	}
	printf("socketfd = %d.\n", sockfd);
	/**
	 * server.out运行，ctrl+c 多次，
	 * 出现， bind: Address already in use 在ubuntu桌面启动终端去测试
	 */
     int on = 1;
    setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));//设置网络属性重用地址

	
	// 第2步：bind绑定sockefd和当前电脑的ip地址&端口号
	seraddr.sin_family = AF_INET;		// 设置地址族为IPv4
	seraddr.sin_port = htons(SERPORT);	// 设置地址的端口号信息,把int32转化为网络字节序  也可用   htons(INADDR_ANY) 用本地IP ，写一个宏
	seraddr.sin_addr.s_addr = inet_addr(SERADDR);	//　设置IP地址转化为网络字节序
/**
 *  设计思想只有有一个,sockaddr 是一个16个字节结构体，
 *  把 sockaddr_in  首地址传入， bind函数内容根据步长来读取ip,端口
 *  这里16个字节就是一个卡槽，可以传递ipv4, ipv6
 *
 */
	ret = bind(sockfd, (const struct sockaddr *)&seraddr, sizeof(seraddr)); // 把ip和port和fd绑定起来
	if (ret < 0)
	{
		perror("bind");
		return -1;
	}
	printf("bind success.\n");
	
	// 第三步：listen监听端口
	/***
	 *  listen(int socket, int backlog)   // backlog 监听队列，10表示， 同时连接到我的个数
	 *   
	 */

	 
	ret = listen(sockfd, BACKLOG);		// 阻塞等待客户端来连接服务器
	if (ret < 0)
	{
		perror("listen  fail");
		return -1;
	}
	// 上面就是三次握手
	// 第四步：accept阻塞等待客户端接入
    struct sockaddr_in cliaddr = {0};
	socklen_t clien_len = sizeof(cliaddr);
	clifd = accept(sockfd, (struct sockaddr *)&cliaddr, &clien_len);
	printf("连接已经建立，client fd = %d.\n", ret);
	if(clifd == -1){
	
		perror("accept error ");
		exit(1);
	}

// 客户端连接服务器以后 ，获取到客户端ip和port
    // int -> char*
    printf("cliient ip: %s, port: %d\n",
           inet_ntop(AF_INET, &cliaddr.sin_addr.s_addr, ipbuf, sizeof(ipbuf)),
           ntohs(cliaddr.sin_port));

    // 通信
    while(1)
    {
        // 先接收数据
        char buf[1024] = {0};
		//阻塞
        int len = read(clifd, buf, sizeof(buf));
        if(len == -1)
        {
            perror("read error");
            break;
        }
        else if(len > 0)
        {
            // 顺利读出了数据
            printf("read buf = %s\n", buf);
            // 小写 -》 大写
            for(int i=0; i<len; ++i)
            {
                buf[i] = toupper(buf[i]);
            }
            printf(" -- toupper: %s\n", buf);

            // 数据发送给客户端
            write(clifd, buf, strlen(buf)+1);
        }
        else if( len == 0 )
        {
            printf("client  close , server  disconnect ...\n");
            break;
        }
    }

    close(sockfd);
    close(clifd);



#if 0
	// 建立连接之后就可以通信了
	// 客户端给服务器发
	ret = recv(clifd, recvbuf, sizeof(recvbuf), 0);
	printf("成功接收了%d个字节\n", ret);
	printf("client发送过来的内容是：%s\n", recvbuf);
#endif 

#if 0
   // 客户端反复给服务器发
	while (1)
	{
		ret = recv(clifd, recvbuf, sizeof(recvbuf), 0);
		//printf("成功接收了%d个字节\n", ret);
		printf("client发送过来的内容是：%s\n", recvbuf);	
		memset(recvbuf, 0, sizeof(recvbuf));
	}
#endif 


   	// 服务器给客户端发
	// strcpy(recvbuf, "hello world.");
	// ret = send(clifd, recvbuf, strlen(recvbuf), 0);
	// printf("发送了%d个字符\n", ret);

	
	return 0;
}


